package com.dman.multi.ui;

import java.awt.Color;
import java.io.File;
import java.io.FileInputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.net.URL;
import java.util.Comparator;

import org.apache.pivot.beans.BXML;
import org.apache.pivot.beans.BXMLSerializer;
import org.apache.pivot.beans.Bindable;
import org.apache.pivot.collections.ArrayList;
import org.apache.pivot.collections.HashMap;
import org.apache.pivot.collections.List;
import org.apache.pivot.collections.Map;
import org.apache.pivot.collections.Sequence.Tree.Path;
import org.apache.pivot.util.Resources;
import org.apache.pivot.util.Vote;
import org.apache.pivot.wtk.Alert;
import org.apache.pivot.wtk.BoxPane;
import org.apache.pivot.wtk.Button;
import org.apache.pivot.wtk.ButtonPressListener;
import org.apache.pivot.wtk.Dialog;
import org.apache.pivot.wtk.DialogStateListener;
import org.apache.pivot.wtk.Label;
import org.apache.pivot.wtk.Prompt;
import org.apache.pivot.wtk.PushButton;
import org.apache.pivot.wtk.Slider;
import org.apache.pivot.wtk.SliderValueListener;
import org.apache.pivot.wtk.TableView;
import org.apache.pivot.wtk.TextInput;
import org.apache.pivot.wtk.TreeView;
import org.apache.pivot.wtk.TreeViewSelectionListener;
import org.apache.pivot.wtk.Window;
import org.apache.pivot.wtk.WindowStateListener;
import org.apache.pivot.wtk.content.ButtonData;

import com.dman.multi.cmd.Command;
import com.dman.multi.cmd.GUIDeleteCommand;
import com.dman.multi.cmd.GUIDownloadStateCommand;
import com.dman.multi.cmd.GUIFileTypeCommand;
import com.dman.multi.cmd.GUIHistoryCommand;
import com.dman.multi.cmd.GUIMoveCommand;
import com.dman.multi.cmd.GUIOpenFolderCommand;
import com.dman.multi.cmd.GUIPauseCommand;
import com.dman.multi.cmd.GUIRestartCommand;
import com.dman.multi.cmd.GUIResumeCommand;
import com.dman.multi.cmd.GUIScheduleCommand;
import com.dman.multi.cmd.GUISearchCommand;
import com.dman.multi.cmd.GUISettingsCommand;
import com.dman.multi.core.Download;
import com.dman.multi.core.DownloadSettings;
import com.dman.multi.core.GlobFilenameFilter;
import com.dman.multi.core.ThrottledNetworkStream;
import com.dman.multi.core.ThrottledNetworkStream.Mode;
import com.dman.multi.core.UIThread;

public class DMan extends Window implements Bindable {
	
	// the toolbar buttons
	@BXML private PushButton addButton;
	@BXML private PushButton scheduleButton;
	@BXML private PushButton deleteButton;
	@BXML private PushButton pauseButton;
	@BXML private PushButton resumeButton;
	@BXML private PushButton restartButton;
	@BXML private PushButton prevButton;
	@BXML private PushButton nextButton;
	@BXML private PushButton searchButton;
	@BXML private PushButton helpButton;
	@BXML private PushButton homeButton;
	@BXML private PushButton openFolderButton;
	@BXML private PushButton settingsButton;
	
	// the toolbar slider
	@BXML private Label sliderMode;
	@BXML private Slider modeSlider;
	
	// the table view in which data is to be displayed
	@BXML private TableView downloadTable;
	
	// the hidden box pane and the controls
	@BXML private BoxPane searchPane;
	@BXML private TextInput searchInput;
	@BXML private PushButton closeSearchButton;
	
	// the tree view controls
	@BXML private TreeView treeView;
	
	private Settings settingsDialog = null;
	
	private static Map<Integer, String> modeTextMap = new HashMap<Integer, String>();
	static{
		modeTextMap.put(ThrottledNetworkStream.Mode.HEAVY.getNumber(), " High");
		modeTextMap.put(ThrottledNetworkStream.Mode.MEDIUM.getNumber(), " Med");
		modeTextMap.put(ThrottledNetworkStream.Mode.LIGHT.getNumber(), " Low");
	}
	private static Map<Integer, Mode> modeMap = new HashMap<Integer, Mode>();
	static{
		modeMap.put(ThrottledNetworkStream.Mode.HEAVY.getNumber(), ThrottledNetworkStream.Mode.HEAVY);
		modeMap.put(ThrottledNetworkStream.Mode.MEDIUM.getNumber(), ThrottledNetworkStream.Mode.MEDIUM);
		modeMap.put(ThrottledNetworkStream.Mode.LIGHT.getNumber(), ThrottledNetworkStream.Mode.LIGHT);
	}
	private static Map<String, Command> commandMap = new HashMap<String, Command>();
	private List<Download> downloadList = new ArrayList<Download>();
	private boolean forceClose = false;

	@Override
	public void initialize(Map<String, Object> namespace, URL location,
			Resources resources) {
		// populate the string command map
		ButtonData buttonData = (ButtonData) addButton.getButtonData();
		commandMap.put(buttonData.getText(), new GUIScheduleCommand(this, downloadList, true));
		buttonData = (ButtonData) scheduleButton.getButtonData();
		commandMap.put(buttonData.getText(), new GUIScheduleCommand(this, downloadList, false));
		buttonData = (ButtonData) deleteButton.getButtonData();
		commandMap.put(buttonData.getText(), new GUIDeleteCommand(this, downloadList));
		buttonData = (ButtonData) pauseButton.getButtonData();
		commandMap.put(buttonData.getText(), new GUIPauseCommand(this, downloadList));
		buttonData = (ButtonData) resumeButton.getButtonData();
		commandMap.put(buttonData.getText(), new GUIResumeCommand(this, downloadList));
		buttonData = (ButtonData) restartButton.getButtonData();
		commandMap.put(buttonData.getText(), new GUIRestartCommand(this, downloadList));
		buttonData = (ButtonData) prevButton.getButtonData();
		commandMap.put(buttonData.getText(), new GUIMoveCommand(downloadTable, downloadList, true));
		buttonData = (ButtonData) nextButton.getButtonData();
		commandMap.put(buttonData.getText(), new GUIMoveCommand(downloadTable, downloadList, false));
		buttonData = (ButtonData) searchButton.getButtonData();
		commandMap.put(buttonData.getText(), new GUISearchCommand(searchPane, searchInput, downloadList));
		buttonData = (ButtonData) openFolderButton.getButtonData();
		commandMap.put(buttonData.getText(), new GUIOpenFolderCommand(this, downloadList));
		buttonData = (ButtonData) settingsButton.getButtonData();
		commandMap.put(buttonData.getText(), new GUISettingsCommand(this));
		
		// setup the handlers for toolbar button pressed
		ButtonPressListener bps = new ButtonPressListener() {			
			@Override
			public void buttonPressed(Button button) {
				ButtonData bd = (ButtonData) button.getButtonData();
				Command cmd = commandMap.get(bd.getText());
				if(cmd != null)
					cmd.execute();
			}
		};
		addButton.getButtonPressListeners().add(bps);
		scheduleButton.getButtonPressListeners().add(bps);
		deleteButton.getButtonPressListeners().add(bps);
		pauseButton.getButtonPressListeners().add(bps);
		resumeButton.getButtonPressListeners().add(bps);
		restartButton.getButtonPressListeners().add(bps);
		prevButton.getButtonPressListeners().add(bps);
		nextButton.getButtonPressListeners().add(bps);
		searchButton.getButtonPressListeners().add(bps);
		helpButton.getButtonPressListeners().add(bps);
		homeButton.getButtonPressListeners().add(bps);
		openFolderButton.getButtonPressListeners().add(bps);
		settingsButton.getButtonPressListeners().add(bps);
		
		// setup the mode slider handler
		sliderMode.setText(modeTextMap.get(modeSlider.getValue()));
		modeSlider.getSliderValueListeners().add(new SliderValueListener() {			
			@Override
			public void valueChanged(Slider slider, int previousValue) {
				int currval = slider.getValue();
				sliderMode.setText(modeTextMap.get(currval));
				ThrottledNetworkStream.setMode(modeMap.get(currval));
			}
		});
		
		// setup the tableview selection background
		StyleDictionary styles = downloadTable.getStyles();
		styles.put("selectionBackgroundColor", Color.MAGENTA);
		styles.put("selectionColor", Color.WHITE);
		
		// setup window close handler
		this.getWindowStateListeners().add(new WindowStateListener.Adapter(){
			@Override
			public Vote previewWindowClose(Window window) {
				// check active downloads which do not support resume
				String str = "";
				Vote v = Vote.APPROVE;
 
				for(Download dl : downloadList)
					if(!dl.isDownloadComplete() && !dl.isResumeSupported())
						str += dl.getFileName() + "\n";
				
				if(str.isEmpty() || forceClose){
					
					// save the downloads
					for(Download d : downloadList){
						try {
							d.pause(true);
						} catch (UnsupportedOperationException e) {
							Prompt.prompt("Resume is not supported with download: " + d.getFileName(), DMan.this);
						} catch (IOException e) {
							Prompt.prompt("Unable to persist download: " + d.getFileName(), DMan.this);
						}
					}
					
					// save the download settings
					try {
						Settings.getSettings().save();
					} catch (IOException e) {
						Prompt.prompt("Unable to persist download settings", DMan.this);
					}
					
					// shutdown the ThreadPoolExecutor
					Download.schedExec.shutdown();
					v = Vote.APPROVE;
				} else {
					final Alert alert = new Alert();
					alert.setModal(true);
					Alert.OptionSequence opseq = alert.getOptions();
					opseq.remove(0, opseq.getLength());
					opseq.add("Yes");
					opseq.add("No");
					alert.getDialogStateListeners().add(new DialogStateListener.Adapter() {
						
						@Override
						public void dialogClosed(Dialog dialog, boolean modal) {
							if(alert.getSelectedOptionIndex() < 1){
								forceClose = true;
								DMan.this.close();
							}
						}
					});
					alert.open(DMan.this);
					
					v = Vote.DEFER;
				}
				return v;
			}
		});
		
		// setup the search pane
		searchPane.setVisible(false);
		closeSearchButton.getButtonPressListeners().add(new ButtonPressListener() {			
			@Override
			public void buttonPressed(Button button) {
				searchPane.setVisible(false);
				synchronized (downloadList) {
					for(Download d: downloadList){
						d.setVisible(true);
					}					
				}
			}
		});
		
		// setup tree view control
		treeView.getTreeViewSelectionListeners().add(new TreeViewSelectionListener.Adapter() {			
			@Override
			public void selectedNodeChanged(TreeView treeView, Object previousSelectedNode) {
				Command cmd = null;
				
				// check if it is all node
				Path path = treeView.getSelectedPath();
				if(path.getLength() == 1){
					if(0 == path.get(0))
						cmd = new GUIFileTypeCommand(downloadList, treeView);
				}
				
				// check if is file type node
				if(cmd == null && path.getLength() > 1){
					if(1 == path.get(0))
						cmd = new GUIFileTypeCommand(downloadList, treeView);
					if(2 == path.get(0))
						cmd = new GUIDownloadStateCommand(downloadList, treeView);
					if(3 == path.get(0))
						cmd = new GUIHistoryCommand(downloadList, treeView);
				}
				
				// check if command is not null execute
				if(cmd != null)
					cmd.execute();
			}
		});
		
		// finally read the downloads and settings
		try{
			File f = new File(Download.FULL_CONF_DIR);
			BXMLSerializer bxmlSerializer = new BXMLSerializer();
			settingsDialog = (Settings) bxmlSerializer.readObject(Settings.class, "dman-settings.bxml");
			if(!f.exists()){
				f.mkdirs();
				this.getWindowStateListeners().add(new WindowStateListener.Adapter(){
					@Override
					public void windowOpened(Window window) {
						settingsDialog.setModal(true);
						settingsDialog.open(DMan.this);					
					}
				});
			}
			
			ObjectInputStream ois = null;
			String dirName = Download.FULL_CONF_DIR + File.separator;
			if(f.isDirectory()){
				// read the download serialized files
				FilenameFilter fnf = new GlobFilenameFilter("*.ser");
				String [] files = f.list(fnf);
				for(String file: files){
					try {
						ois = new ObjectInputStream(new FileInputStream(dirName + file));
						Download d = (Download) ois.readObject();
						downloadList.add(d);
					} catch (IOException e) {
						continue;
					} catch (ClassNotFoundException e) {
						continue;
					}
				}
				
				// sort the download objects
				if(files.length > 0){
					Comparator<Download> compDownload = new Comparator<Download>() {
						@Override
						public int compare(Download d1, Download d2) {
							long time1 = d1.getStartDate().getTimeInMillis();
							long time2 = d2.getStartDate().getTimeInMillis();
							if(time1 == time2)
								return 0;
							if(time1 < time2)
								return -1;
							
							return 1;
						}
					};
					ArrayList.sort((ArrayList<Download>)downloadList, compDownload);
				}
				
				// setup the settings
				fnf = new GlobFilenameFilter("*.settings");
				files = f.list(fnf);
				for(String file: files){
					try {
						ois = new ObjectInputStream(new FileInputStream(dirName + file));
						DownloadSettings ds = (DownloadSettings) ois.readObject();
						Settings.setSettings(settingsDialog, ds);
						break;
					} catch (Exception e) {
						throw new ClassCastException("Unable to read download settings");
					}
				}
			}
		} catch(Exception e){
			e.printStackTrace();
		}
		
		
		// setup the UIThread
		Thread uiThread = new Thread(new UIThread(downloadList, downloadTable));
		uiThread.setDaemon(true);
		uiThread.start();
	}

	public int getSelectedTableViewIndex() {
		return downloadTable.getSelectedIndex();
	}
	
	public Settings getSettingsDialog(){
		return settingsDialog;
	}
}
